package com.antcom.antread.common.log.aspect;

import com.alibaba.fastjson.JSON;
import com.antcom.antread.api.domain.OperationLog;
import com.antcom.antread.common.core.utils.ServletUtils;
import com.antcom.antread.common.core.utils.ip.IpUtils;
import com.antcom.antread.common.core.utils.string.StringUtils;
import com.antcom.antread.common.log.annotation.Log;
import com.antcom.antread.common.log.enums.BusinessMsg;
import com.antcom.antread.common.log.service.AsyncLogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpMethod;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Date;


/**
 * 配置日志切面
 *
 * @Author 中国蚂蚁技术有限公司
 * @create 2020/12/24 20:18
 */

@Component
@Aspect
public class LogAspect {

    /**
     * 日志记录
     */
    private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);

    @Resource
    private AsyncLogService asyncLogService;

    /**
     * 配置植入点
     */
    @Pointcut("@annotation(com.antcom.antread.common.log.annotation.Log)")
    public void controllerAspect() {
    }

    /**
     * 处理完请求后执行
     *
     * @param joinPoint 切点
     */
    @AfterReturning(pointcut = "controllerAspect()", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) {
        handleLog(joinPoint, null, jsonResult);
    }

    /**
     * 拦截异常操作
     *
     * @param joinPoint 切点
     * @param e         异常
     */
    @AfterThrowing(value = "controllerAspect()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
        handleLog(joinPoint, e, null);
    }

    /**
     * 日志业务处理
     *
     * @param joinPoint  切点
     * @param e          异常
     * @param jsonResult 返回值
     */
    protected void handleLog(final JoinPoint joinPoint, final Exception e, Object jsonResult) {

        try {
            // 获取注解
            Log log = getAnnotationLogger(joinPoint);
            if (log == null) {
                return;
            }

            // 数据库日志
            OperationLog operationLog = new OperationLog();

            // 状态标识
            operationLog.setMsg(BusinessMsg.SUCCESS.toString());
            if (e != null) {
                operationLog.setMsg(BusinessMsg.FAIL.toString());
            }
            String ipAddr = IpUtils.getIpAddr(ServletUtils.getRequest());
            // 获取请求ip地址
            operationLog.setRequestIp(ipAddr);

            // 获取请求方法
            operationLog.setRequestMethod(ServletUtils.getRequest().getMethod());

            // 获取请求路径
            operationLog.setRequestUrl(ServletUtils.getRequest().getRequestURI());

            // 获取响应参数
            operationLog.setResponseParam(JSON.toJSONString(jsonResult));

            // 处理注解参数
            getControllerMethodAndDescription(joinPoint, log, operationLog);

            // 操作日期
            operationLog.setOperationDate(new Date());

            // 保存数据库
            asyncLogService.saveLog(operationLog);
        } catch (Exception exp) {
            logger.error("异常信息:{}", exp.getMessage());
            exp.printStackTrace();
        }
    }

    /**
     * 查看注解是否存在
     *
     * @param joinPoint 切点
     * @return log.class
     */
    private Log getAnnotationLogger(JoinPoint joinPoint) {

        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if (method != null) {
            return method.getAnnotation(Log.class);
        }
        return null;
    }


    /**
     * 获取注解中对方法的描述信息 用于Controller层注解
     *
     * @param joinPoint    切点
     * @param log          日志注解
     * @param operationLog 日志对象
     */
    public void getControllerMethodAndDescription(JoinPoint joinPoint, Log log, OperationLog operationLog) {

        // 设置业务操作类型
        operationLog.setFunctionType(log.functionType().toString());

        // 设置标题
        operationLog.setLogTitle(log.title());

        // 设置操作人类别
        operationLog.setOperatorType(log.operatorType().toString());

        // 确定是否需要request,参数和值
        if (log.isSaveRequestData() == true) {
            //获取请求参数，保存到数据库中
            saveRequestParam(joinPoint, operationLog);
        }
    }

    /**
     * 保存请求参数
     *
     * @param joinPoint    切点
     * @param operationLog 日志对象
     */
    public void saveRequestParam(JoinPoint joinPoint, OperationLog operationLog) {

        // 获取请求方法
        String requestMethod = operationLog.getRequestMethod();
        if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {
            String params = argsArrayToString(joinPoint.getArgs());
            operationLog.setRequestParam(StringUtils.substring(params, 0));
        }
    }


    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray) {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0) {
            for (int i = 0; i < paramsArray.length; i++) {
                if (!isFilterObject(paramsArray[i])) {
                    try {
                        Object jsonObj = JSON.toJSON(paramsArray[i]);
                        params += jsonObj.toString() + " ";
                    } catch (Exception e) {
                    }
                }
            }
        }
        return params.trim();
    }


    /**
     * 判断是否需要过滤的对象。
     *
     * @param o 对象信息。
     * @return 如果是需要过滤的对象，则返回true；否则返回false。
     */
    public boolean isFilterObject(final Object o) {
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse;
    }
}
